﻿using System;
using System.Collections.Generic;
using System.Linq;

using zijian666.Abstractions;
using zijian666.Core;
using zijian666.ConvertExtensions;

namespace zijian666.Convertor.Base;

/// <summary>
/// 聚合转换器
/// </summary>
/// <typeparam name="T"></typeparam>
/// <remarks>
/// 如果转换失败,会继续尝试下一个转换器,直到成功或全部失败
/// </remarks>
public sealed class AggregateConvertor<T> : BaseConvertor<T>, IConvertor<T>
{
    /// <summary>
    /// 构造函数
    /// </summary>
    /// <exception cref="ArgumentOutOfRangeException">转换器必须大于1个</exception>
    public AggregateConvertor(IEnumerable<IConvertor<T>> convertors)
    {
        if (convertors is null)
        {
            throw new ArgumentNullException(nameof(convertors));
        }

        InnerConvertors = convertors.Where(x => x is not AggregateConvertor<T>).Union(
                        convertors.OfType<AggregateConvertor<T>>().SelectMany(x => x.InnerConvertors)
                     ).ToList().AsReadOnly();
        if (InnerConvertors.Count < 2)
        {
            throw new ArgumentOutOfRangeException(nameof(convertors), "转换器必须大于1个");
        }
    }

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <exception cref="ArgumentOutOfRangeException">转换器必须大于1个</exception>
    public AggregateConvertor(params IConvertor<T>[] convertors)
        : this((IEnumerable<IConvertor<T>>)convertors)
    {

    }

    /// <summary>
    /// 转换器集合
    /// </summary>
    public IReadOnlyCollection<IConvertor<T>> InnerConvertors { get; }

    /// <inheritdoc />
    public override ConvertResult<T> Convert(IConvertContext context, object? input)
    {
        if (context is null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        ExceptionBuilder? exbuilder = null;
        foreach (var convertor in InnerConvertors)
        {
            var result = convertor.Convert(context, input);
            if (result.Success)
            {
                return result;
            }
            ExceptionBuilder.Catch(ref exbuilder, result.Exception, "AggregateConvertor:" + convertor.GetType().GetFriendlyName());
        }

        var message = ExceptionHelper.ConvertFailMessage(input, TypeFriendlyName, context.Settings.CultureInfo);
        return exbuilder!.ToException(message);
    }
}
